/*
 * RouteNetwork.java
 *
 * Created on February 25, 2007, 9:02 PM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.routenet;

import org.fpdev.core.transit.SubRoute;
import org.fpdev.core.transit.SubRouteInfo;
import org.fpdev.core.transit.Routes;
import org.fpdev.core.transit.Route;
import org.fpdev.core.basenet.BLink;
import org.fpdev.core.basenet.Path;
import org.fpdev.core.basenet.BaseNetwork;
import org.fpdev.core.basenet.BNode;
import org.fpdev.core.transit.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import java.util.Map;
import org.fpdev.core.*;
import org.fpdev.core.basenet.*;

import org.fpdev.apps.rtemaster.GUIOutputAcceptor;
import org.fpdev.apps.rtemaster.gui.map.MapCanvas;
import org.jgrapht.graph.DirectedPseudograph;

/**
 *
 * @author demory
 */
public class RouteNetwork {

  private FPEngine engine_;
  private DirectedPseudograph<RNode, RLink> rGraph_;
  private BaseNetwork baseNet_;
  private Map<String, RNode> nodes_;
  private RNode startRNode_,  endRNode_;
  private boolean startOffNet_,  endOffNet_;
  private List<RLink> boardLinks_,  alightLinks_;
  private List<RLink> drawPathRLinks_;

  /**
   * Creates a new instance of CRouteNetwork
   */
  public RouteNetwork(FPEngine engine) {
    engine_ = engine;
    baseNet_ = engine_.getBaseNet();
    rGraph_ = new DirectedPseudograph(RLink.class);
    nodes_ = new HashMap<String, RNode>();

    startRNode_ = endRNode_ = null;
    startOffNet_ = endOffNet_ = false;
    boardLinks_ = new LinkedList<RLink>();
    alightLinks_ = new LinkedList<RLink>();

    drawPathRLinks_ = null;
  }

  //public DirectedPseudograph getRGraph() { return rGraph_; }
  public void build(Routes routes) {
    GUIOutputAcceptor gui = engine_.getGUIOutputAcceptor();
    int subsBuilt = 0, totalSubs = routes.subRouteCount();
    for(Route rte : routes.getCollection()) {
      for(SubRoute sub : rte.getSubRoutes()) {
        buildSubRoute(sub);
        subsBuilt++;
        double pctWritten = 100.0 * (double) subsBuilt / (double) totalSubs;
        if (gui != null) {
          gui.setProgress((int) pctWritten);
        }
      }
    }
    if (gui != null) {
      gui.setProgress((int) 0);
    }
  }

  public void buildSubRoute(SubRoute sub) {
    //System.out.println("building sub network: "+sub.getFullID());
    Path dp = sub.getPath().getDirectPath();
    if (dp.linkCount() == 0) {
      return;
    }
    BNode fromNode = dp.startNode(), toNode;
    Iterator<BLink> links = dp.getLinks();
    Iterator<SubRouteInfo> sris;
    RNode lastRNode = checkNode(fromNode), fromRNode, toRNode;
    int dli = 0, lastStartDLI = 0;
    while (links.hasNext()) {
      BLink curLink = links.next();
      toNode = baseNet_.opposite(fromNode, curLink);
      //int dli = dp.getFirstPLI(curLink, fromNode);
      //System.out.println("dli:"+dli);

      if (curLink != dp.endLink()) {
        if (toNode.isStation()) {
          //if(sub.getFullID().equals("NE-S:NB")) System.out.println(" STATION NODE "+toNode.getID());
          fromRNode = lastRNode;
          toRNode = checkNode(toNode);
          //System.out.println("cSRL a");
          createSubRouteLink(sub, fromRNode, toRNode, lastStartDLI, dli);
          lastRNode = toRNode;
          lastStartDLI = dli + 1;
        } else {
          int oSubs = 0, iSubs = 0;
          List<SubRoute> oSubSeq = new LinkedList<SubRoute>();
          List<SubRoute> iSubSeq = new LinkedList<SubRoute>();
          BLink nextLink = dp.after(curLink, fromNode);
          sris = engine_.getOutSubRouteInfo(toNode);
          while (sris.hasNext()) {
            SubRouteInfo sri = sris.next();
            if (sri.getDLI() == -1) {
              continue;
            }
            //System.out.println("out sri "+sri.getSubRoute().getID());
            if (sri.getSubRoute().getRoute() != sub.getRoute()) {
              // check if departing d-links coincide
              BLink oLink = sri.getSubRoute().getPath().getDirectPath().getLink(sri.getDLI());
              if (!dLinksEqual(toNode, oLink, curLink) && !dLinksEqual(toNode, oLink, nextLink)) {
                oSubs++;
                oSubSeq.add(sri.getSubRoute());
              }
            }

          }
          sris = engine_.getInSubRouteInfo(toNode);
          while (sris.hasNext()) {
            SubRouteInfo sri = sris.next();
            if (sri.getDLI() == -1) {
              continue;
            }
            //System.out.println("in sri "+sri.getSubRoute().getID());
            //CSubRoute iSub = ((CSubRouteInfo) sris.nextObject()).getSubRoute();
            if (sri.getSubRoute().getRoute() != sub.getRoute()) {
              BLink iLink = sri.getSubRoute().getPath().getDirectPath().getLink(sri.getDLI());
              if (!dLinksEqual(toNode, iLink, curLink) && !dLinksEqual(toNode, iLink, nextLink)) {
                iSubs++;
                iSubSeq.add(sri.getSubRoute());
              }
            }
          }
          if (iSubs > 0 || oSubs > 0) {
            fromRNode = lastRNode;
            toRNode = checkNode(toNode);
            //System.out.println("cSRL b");
            createSubRouteLink(sub, fromRNode, toRNode, lastStartDLI, dli);
            lastRNode = toRNode;
            lastStartDLI = dli + 1;
          }
        }
      /*if(iSubs > 0) {
      System.out.println(" iSubs="+iSubs);
      ObjectIterator subs = iSubSeq.elements();
      while(subs.hasNext()) {
      System.out.println("  - "+((CSubRoute) subs.nextObject()).getFullID());
      }
      }
      if(oSubs > 0) {
      System.out.println(" oSubs="+oSubs);
      ObjectIterator subs = oSubSeq.elements();
      while(subs.hasNext()) {
      System.out.println("  - "+((CSubRoute) subs.nextObject()).getFullID());
      }
      }*/

      } else { // handle final link
        fromRNode = lastRNode;
        toRNode = checkNode(toNode);
        createSubRouteLink(sub, fromRNode, toRNode, lastStartDLI, dli);
      }

      // advance to next link
      fromNode = toNode;
      dli++;
    }
  }

  private boolean dLinksEqual(BNode fNode, BLink link1, BLink link2) {
    BNode tNode1 = baseNet_.opposite(fNode, link1);
    BNode tNode2 = baseNet_.opposite(fNode, link2);
    if (tNode1.isStation() && tNode2.isStation()) {
      return (tNode1.getStation() == tNode2.getStation());
    }
    if (link1.getFNodeID() == link2.getFNodeID() && link1.getTNodeID() == link2.getTNodeID()) {
      return true;
    }
    if (link1.getFNodeID() == link2.getTNodeID() && link1.getTNodeID() == link2.getFNodeID()) {
      return true;
    }
    return false;
  }

  public RNode getRouteNode(BNode node) {
    return nodes_.get(nodeKey(node));
  }

  public RNode getStartRouteNode(BNode node) {
    startRNode_ = getRouteNode(node);
    return startRNode_;
  }

  public RNode getEndRouteNode(BNode node) {
    endRNode_ = getRouteNode(node);
    return endRNode_;
  }

  private RNode checkNode(BNode node) {
    //System.out.println("check node "+(node == null ? "null" : node.getID()));
    String key = nodeKey(node);
    RNode rNode = nodes_.get(key);
    if (rNode == null) {
      rNode = node.isStation() ? new RNodeStation(node.getStation()) : new RNodeStandard(node);
      rGraph_.addVertex(rNode);
      nodes_.put(key, rNode);
    //System.out.println(" CREATE NODE: "+key);
    }
    return rNode;
  }

  private String nodeKey(BNode node) {
    return node.isStation() ? "s_" + node.getStation().getID() : "" + node.getID();
  }

  public boolean isOnNetwork(BNode node) {
    return nodes_.containsKey(nodeKey(node));
  }

  public int nodeCount() {
    return rGraph_.vertexSet().size();
  }

  public RNode opposite(RNode node, RLink link) {
    if (rGraph_.getEdgeSource(link) == node) {
      return rGraph_.getEdgeTarget(link);
    }
    if (rGraph_.getEdgeTarget(link) == node) {
      return rGraph_.getEdgeSource(link);
    }
    return null;
  }

  public Iterator<RLink> outLinks(RNode node) {
    return rGraph_.outgoingEdgesOf(node).iterator();
  }

  public Iterator<RLink> inLinks(RNode node) {
    return rGraph_.incomingEdgesOf(node).iterator();
  }

  public RNode fromNode(RLink link) {
    return rGraph_.getEdgeSource(link);
  }

  public RNode toNode(RLink link) {
    return rGraph_.getEdgeTarget(link);
  }

  private void createSubRouteLink(SubRoute sub, RNode fRNode, RNode tRNode, int sDLI, int eDLI) {
    RLinkSub link = new RLinkSub(sub, fRNode, tRNode, sDLI, eDLI);
    rGraph_.addEdge(fRNode, tRNode, link);
    //Edge edge = rGraph_.insertDirectedEdge(fRNode.getVertex(), tRNode.getVertex(), link);
    //link.setEdge(edge);
    sub.addRouteLink(link);
  //if(sub.getFullID().equals("NE-S:NB")) System.out.println(" CREATE LINK: "+fRNode.getID()+" ("+sDLI+") to "+tRNode.getID()+" ("+eDLI+")");

  }

  public RNode initStartNode(BNode node) {
    RNode rNode = new RNodeStandard(node);
    rGraph_.addVertex(rNode);
    //Vertex v = rGraph_.insertVertex(rNode);
    //rNode.setVertex(v);
    nodes_.put(nodeKey(node), rNode);
    startRNode_ = rNode;
    startOffNet_ = true;
    return startRNode_;
  }

  public RNode initEndNode(BNode node) {
    RNode rNode = new RNodeStandard(node);
    rGraph_.addVertex(rNode);
    //Vertex v = rGraph_.insertVertex(rNode);
    //rNode.setVertex(v);
    nodes_.put(nodeKey(node), rNode);
    endRNode_ = rNode;
    endOffNet_ = true;
    return endRNode_;
  }

  public RLinkBoard createBoardLink(BNode startSNode, SubRoute sub, int dli, List<BLink> accessLinks) {
    RLinkBoard bLink = new RLinkBoard(startSNode, sub.getRouteLinkFromDLI(dli), dli, accessLinks);
    rGraph_.addEdge(startRNode_, bLink.getEndRouteNode(), bLink);
    //Edge edge = rGraph_.insertDirectedEdge(startRNode_.getVertex(), bLink.getEndRouteNode().getVertex(), bLink);
    //bLink.setEdge(edge);
    boardLinks_.add(bLink);
    return bLink;
  }

  public RLinkAlight createAlightLink(BNode endSNode, SubRoute sub, int dli, List<BLink> accessLinks) {
    RLinkAlight aLink = new RLinkAlight(endSNode, sub.getRouteLinkFromDLI(dli), dli, accessLinks, engine_);
    //System.out.println(" - aL.gSRN: "+aLink.getStartRouteNode().getID());
    //System.out.println(" - eRN_: "+endRNode_.getID());
    rGraph_.addEdge(aLink.getStartRouteNode(), endRNode_, aLink);
    //Edge edge = rGraph_.insertDirectedEdge(aLink.getStartRouteNode().getVertex(), endRNode_.getVertex(), aLink);
    //aLink.setEdge(edge);
    alightLinks_.add(aLink);
    return aLink;
  }

  public void removeLink(RLink link) {
    rGraph_.removeEdge(link);
  }

  public void clearPathSearchElements() {
    Iterator<RLink> bLinks = boardLinks_.iterator();
    while (bLinks.hasNext()) {
      RLink rlink = bLinks.next();
      rGraph_.removeEdge(rlink);
    }
    boardLinks_.clear();

    Iterator<RLink> aLinks = alightLinks_.iterator();
    while (aLinks.hasNext()) {
      RLink rlink = aLinks.next();
      rGraph_.removeEdge(rlink);
    }
    alightLinks_.clear();

    if (startOffNet_) {
      rGraph_.removeVertex(startRNode_);
      nodes_.remove(startRNode_.getID());
      startOffNet_ = false;
    }
    if (endOffNet_) {
      rGraph_.removeVertex(endRNode_);
      nodes_.remove(endRNode_.getID());
      endOffNet_ = false;
    }
    startRNode_ = endRNode_ = null;
    drawPathRLinks_ = null;
  }

  // DRAW METHODS
  public void setDrawPath(List<RLink> links) {
    drawPathRLinks_ = links;
  }

  public void draw(MapCanvas canvas) {
    //Color drawColor = new Color(127,127,255);
    Iterator<RLink> links = rGraph_.edgeSet().iterator();
    while (links.hasNext()) {
      RLink link = links.next();
      link.draw(canvas);
    //panel.drawLine(link.getFromNode().getX(), link.getFromNode().getY(), link.getToNode().getX(), link.getToNode().getY(), true);
    }
    Iterator<RNode> nodes = rGraph_.vertexSet().iterator();
    while (nodes.hasNext()) {
      RNode node = nodes.next();
      canvas.setColor(new Color(127, 127, 255));
      canvas.drawPoint(node.getX(), node.getY(), 10);
      canvas.setColor(Color.white);
      canvas.drawPoint(node.getX(), node.getY(), 6);
    }
    if (drawPathRLinks_ != null) {
      System.out.println("drawing rl-dpath");
      canvas.setStroke(new BasicStroke(6));
      Iterator<RLink> pathLinks = drawPathRLinks_.iterator();
      while (pathLinks.hasNext()) {
        RLink link = pathLinks.next();
        link.draw(canvas);
        System.out.println(" drawing link: " + link.getID());
      }
    }
  }
}
